package com.example.sample.vpn;

import android.content.Context;
import android.os.Handler;
import android.util.Log;

import net.liteheaven.xfirewall.MyApp;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;

public class PacketForwarder {

    private static final String TAG = "PacketForwarder";
    private byte[] buffer = new byte[1024];
    private Context mCtx;
    private Handler mHandler;
    private UdpListenThread mUdpThread;
    private TcpListenThread mTcpThread;
    public static int UDP_FORWARD_PORT = 36002;
    public static final int UDP_GAME_SERVER_PORT = 36002;
    public static int TCP_FORWARD_PORT = 36012;
    public static final int TCP_GAME_SERVER_PORT = 36012;

    public static interface ForwardListener{
        public void onLog(String log);
    }

    private static ForwardListener mListener;

    public static void setListener(ForwardListener l){
        mListener = l;
    }

    private static PacketForwarder mInstance;
    public static PacketForwarder getInstance(){
        if (mInstance == null){
            mInstance = new PacketForwarder(MyApp.getInstance().getApplicationContext());
        }
        return mInstance;
    }

    private class UdpListenThread extends Thread{

        private boolean mRunning = true;
        DatagramSocket mDs;

        public void stopIt(){
            if (mRunning){
                mRunning = false;
                mDs.close();
                try {
                    join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void run() {
            final String serverHost = "0.0.0.0";
            final int serverPort = UDP_FORWARD_PORT;

            InetSocketAddress socketAddress = new InetSocketAddress(serverHost, serverPort);
            try {
                mDs = new DatagramSocket(socketAddress);
            } catch (SocketException e) {
                e.printStackTrace();
                mDs = null;
            }

            if (mDs != null){
                mRunning = true;
                while (mRunning) {
                    DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                    String info;
                    try {
                        mDs.receive(packet);
                        info = new String(packet.getData(), 0, packet.getLength());
                        mDs.send(packet);
                    } catch (IOException e) {
                        e.printStackTrace();
                        info = new String(e.getMessage());
                    }
                    writeLog("recv " + info);
                }
            }

            mDs = null;
            mRunning = false;
        }
    }

    private class TcpListenThread extends Thread{

        private boolean mRunning = true;

        public void stopIt(){
            if (mRunning){
                mRunning = false;
                try {
                    join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void run() {
            ServerSocket ss;
            try {
                ss = new ServerSocket(TCP_FORWARD_PORT);
            } catch (IOException e) {
                e.printStackTrace();
                ss = null;
            }

            if (ss == null){
                return;
            }

            mRunning = true;
            while (mRunning) {

                Socket clientSocket = null;
                try {
                    clientSocket = ss.accept();
                } catch (IOException e) {
                    e.printStackTrace();
                    clientSocket = null;
                }

                if (clientSocket == null){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                    }
                    continue;
                }

                writeLog("accept " + clientSocket.getInetAddress().getHostAddress());

                String info;
                try {
                    //获取输入流
                    BufferedReader in = new BufferedReader(
                            new InputStreamReader(clientSocket.getInputStream()));
                    
                    while(true){
                    	info = in.readLine();
                    	writeLog("recv " + info);
                    	if (info == null){
                            break;
                        }
                    }                    
                } catch (Exception e) {
                    info = e.getMessage();
                    break;
                }

                try {
                    clientSocket.close();
                } catch (IOException e) {
                }

                writeLog("peer close!");
            }

            mRunning = false;
        }
    }

    public PacketForwarder(Context ctx){
        mCtx = ctx;
        mHandler = new Handler();
    }

    public void startListenUdp(int port){
        UDP_FORWARD_PORT = port;
        Log.i(TAG, "startListenUdp on " + port);
        if (mUdpThread == null){
            mUdpThread = new UdpListenThread();
            mUdpThread.start();
        }
    }

    public void stopListenUdp(){
        if (mUdpThread!=null){
            mUdpThread.stopIt();
            mUdpThread = null;
        }
    }

    public static void sendUdp(String srvHost, int srvPort, String content){
        byte [] b = content.getBytes();
        byte[] buffer = new byte[1024];

        DatagramSocket ds = null;
        try {
            ds = new DatagramSocket(8887);
            ds.setSoTimeout(2000);

            InetAddress ia = InetAddress.getByName(srvHost);

            Log.i(TAG, "IPV4 = " + (ia instanceof Inet4Address));

            DatagramPacket dp = new DatagramPacket(b, b.length, ia, srvPort);
            //ds.bind(new InetSocketAddress(8887));

            ds.send(dp);
            writeLog("send udp to "+srvHost);

            DatagramPacket dpRecv = new DatagramPacket(buffer, buffer.length);
            ds.receive(dpRecv);
            String strRecv = new String(dpRecv.getData(), 0, dpRecv.getLength());
            writeLog("recv "+ strRecv);
        } catch (Exception e) {
            writeLog("send udp failed! --> " + e.getCause());
        } finally {
            if (ds != null){
                ds.close();
            }
        }
    }

    public void startListenTcp(int port){
        TCP_FORWARD_PORT = port;
        Log.i(TAG, "startListenTcp on " + port);
        if (mTcpThread == null){
            mTcpThread = new TcpListenThread();
            mTcpThread.start();
        }
    }

    public void stopListenTcp(){
        if (mTcpThread!=null){
            mTcpThread.stopIt();
            mTcpThread = null;
        }
    }

    public static void sendTcp(String srvHost, int srvPort, String content){
        Socket socket = null;
        try {
            writeLog("sendTcp ...");
            InetAddress ia = InetAddress.getByName("0.0.0.0");
            socket = new Socket();
            InetSocketAddress isa = new InetSocketAddress(8886);
            writeLog("bind ...");
            //socket.bind(isa);
            SocketAddress dstAddr = new InetSocketAddress(srvHost, srvPort);
            writeLog("connect " + srvHost + ":"+srvPort);
            socket.connect(dstAddr, 1000);

            SocketAddress dstSa = socket.getRemoteSocketAddress();
            writeLog("real dst = " + dstSa.toString());

            //第二个参数为True则为自动flush
            PrintWriter out = new PrintWriter(
                    new BufferedWriter(new OutputStreamWriter(
                            socket.getOutputStream())), true);

            for (int i = 0; i<1; i++){
                writeLog("send tcp to "+srvHost + " " + i);
                out.println(content);
                out.flush();
                Thread.sleep(1000);
            }
            writeLog("send tcp over");
        } catch (Exception e) {
            writeLog("send tcp failed! -> " + e.getMessage());
        }
        finally {
            if (socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void writeLog(String log){
        if (mListener != null){
            mListener.onLog("[Forwarder] " + log);
        }else{
            Log.i(TAG, "[Forwarder] "+ log);
        }

        /*final String infof = info;
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                MiuiToast.cancelLastToast();
                ToastFactory.makeText(mCtx, infof, Toast.LENGTH_SHORT, 1).show();
            }
        });*/
    }
}
